package com.lecyon.farm.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.lecyon.farm.base.BaseRequestVo;
import com.lecyon.farm.base.BaseResponse;
import com.lecyon.farm.base.RestResponse;
import com.lecyon.farm.common.CommonService;
import com.lecyon.farm.common.constant.CommonConstant;
import com.lecyon.farm.common.constant.FarmSysConstant;
import com.lecyon.farm.common.enums.SysRoleEnum;
import com.lecyon.farm.common.exception.FarmException;
import com.lecyon.farm.entity.*;
import com.lecyon.farm.entity.elasticsearch.SimpleGps;
import com.lecyon.farm.service.*;
import com.lecyon.farm.util.RedisUtils;
import com.lecyon.farm.util.ResultUtil;
import com.lecyon.farm.util.StringHelper;
import com.lecyon.farm.util.ValidateUtils;
import com.lecyon.farm.vo.data.*;
import com.lecyon.farm.vo.query.QueryFmCarVo;
import com.lecyon.farm.vo.query.QueryFmWorkDetailsVo;
import com.lecyon.farm.vo.query.QueryFmWorkVo;
import com.lecyon.farm.vo.query.QuerySimpleGpsVo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 数据大屏展示数据
 *
 * @author Yao Zheng
 * @Date 2020/5/18 17:29
 */
@Service
@Validated
public class BigScreenDataService {

    private static final Logger LOG = LoggerFactory.getLogger(BigScreenDataService.class);

    private IFmCarService fmCarService;

    private IFmWorkTotalService fmWorkTotalService;

    private IFmWorkService fmWorkService;

    private IFmWorkDetailsService fmWorkDetailsService;

    private IJsSysEmployeeService jsSysEmployeeService;

    private IJsSysOfficeService jsSysOfficeService;

    private RedisUtils redisUtils;

    private CommonService commonService;

    private ISimpleGpsService simpleGpsService;

    private static final String DEFAULT_BIG_DECIMAL_VALUE = "0";

    private static final Map<String, String> WORK_TYPE_MAP = FarmSysConstant.SYS_JOB_TYPE_MAP;

    /**
     * 根据登录token查询所有作业类型
     *
     * @param vo
     * @return
     */
    public List<String> getAllWorkType(BaseRequestVo<QueryFmWorkVo> vo) {
        List<String> result = Lists.newLinkedList();
        String token = vo.getUserToken();
        List<FmWork> fmWorkList = getFmWorkListByToken(token);
        if (CollectionUtils.isNotEmpty(fmWorkList)) {
            List<String> finalResultList = result;
            fmWorkList.stream().forEach(fmWork -> {
                if (StringHelper.isNotEmpty(fmWork.getType())) {
                    if (WORK_TYPE_MAP.containsKey(fmWork.getType())) {
                        finalResultList.add(WORK_TYPE_MAP.get(fmWork.getType()));
                    }
                }
            });
            result = result.stream().distinct().collect(Collectors.toList());
        }
        return result;
    }

    /**
     * 根据登录token查询所有区域名称
     *
     * @param vo
     * @return
     */
    public RestResponse<List<String>> getAllAreaName(BaseRequestVo<QueryFmWorkVo> vo) {
        RestResponse<List<String>> result = new RestResponse<>();
        List<String> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        JsSysUser sysUser = commonService.findUserByToken(token);
        if (ValidateUtils.isNullOrEmpty(sysUser)) {
            return ResultUtil.success(result, resultList);
        }
        String systemAdmin = "system";
        String userCode = sysUser.getUserCode();
        JsSysEmployee jsSysEmployee = jsSysEmployeeService.getById(userCode);
        String officeCode = null;
        if (ValidateUtils.isNotNullOrEmpty(jsSysEmployee) && StringHelper.isNotEmpty(jsSysEmployee.getOfficeCode())) {
            officeCode = jsSysEmployee.getOfficeCode();
        }
        int userLevel = 0;
        QueryWrapper<JsSysOffice> queryCondition = new QueryWrapper<>();
        List<JsSysOffice> jsSysOfficeList = null;
        if (SysRoleEnum.MASTER.getLevel().equals(sysUser.getSign())) {
            // 机构管理员 可以看到所辖区域内所有相关信息
            // GR代表个人用户
            String specialOfficeCode = "GR";
            if (StringHelper.isNotEmpty(officeCode) && !specialOfficeCode.equals(officeCode)) {
                JsSysOffice jsSysOffice = jsSysOfficeService.getById(officeCode);
                if (ValidateUtils.isNotNullOrEmpty(jsSysOffice) && StringHelper.isNotEmpty(jsSysOffice.getParentCodes())) {
                    String parentCodes = jsSysOffice.getParentCodes();
                    if (parentCodes.contains(CommonConstant.HARDWARE_RESPONSE_SPLIT_STR)) {
                        String[] parentCode = parentCodes.split(CommonConstant.HARDWARE_RESPONSE_SPLIT_STR);
                        int codeLength = parentCode.length;
                        switch (codeLength) {
                            case 1:
                                // 省级机构
                                userLevel = 1;
                                break;
                            case 2:
                                // 市级机构
                                userLevel = 2;
                                break;
                            case 3:
                                // 区县机构
                                userLevel = 3;
                                break;
                        }
                        queryCondition.eq("parent_code", officeCode).eq("office_type", userLevel + 1);
                    }
                }
            }
            jsSysOfficeList = jsSysOfficeService.list(queryCondition);

        } else if (systemAdmin.equals(sysUser.getUserCode())) {
            //系统管理员
            queryCondition.eq("office_type", 1);
            jsSysOfficeList = jsSysOfficeService.list(queryCondition);
        }
        if (CollectionUtils.isNotEmpty(jsSysOfficeList)) {
            jsSysOfficeList.stream().forEach(jsSysOffice -> {
                resultList.add(jsSysOffice.getOfficeName());
            });
        }
        return ResultUtil.success(result, resultList);
    }

    /**
     * 按作业类型统计作业量
     *
     * @return
     */
    public RestResponse<List<WorkVolumeByType>> getTotalWorkDataGroupByWorkType(BaseRequestVo<QueryFmWorkVo> vo) {
        RestResponse<List<WorkVolumeByType>> result = new RestResponse();
        List<WorkVolumeByType> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        JsSysUser sysUser = commonService.findUserByToken(token);
        if (ValidateUtils.isNullOrEmpty(sysUser)) {
            return ResultUtil.error(result, CommonConstant.ASSOCIATED_DATA_MISS, "找不到登录用户相关数据");
        }
        String userCode = sysUser.getUserCode();
        JsSysEmployee jsSysEmployee = jsSysEmployeeService.getById(userCode);
        String officeCode = null;
        if (ValidateUtils.isNotNullOrEmpty(jsSysEmployee) && StringHelper.isNotEmpty(jsSysEmployee.getOfficeCode())) {
            officeCode = jsSysEmployee.getOfficeCode();
        }
        boolean redisFlag;
        if (StringHelper.isNotEmpty(officeCode)) {
            redisFlag = redisUtils.hHasKey(FarmSysConstant.REDIS_KEY_STATISTIC_TOTAL_WORK_DATA, officeCode);
            if (redisFlag) {
                String jsonString = (String) redisUtils.hGet(FarmSysConstant.REDIS_KEY_STATISTIC_TOTAL_WORK_DATA, officeCode);
                if (StringHelper.isNotEmpty(jsonString)) {
                    resultList = JSON.parseArray(jsonString, WorkVolumeByType.class);
                    return ResultUtil.success(result, resultList);
                }
            }
        }
        List<FmWork> fmWorkList = getFmWorkListByToken(token);
        BigDecimal entityArea;
        BigDecimal currentArea;
        Map<String, BigDecimal> workVolumeMap = Maps.newHashMap();
        if (CollectionUtils.isNotEmpty(fmWorkList)) {
            String type;
            // 如果FMWork集合不为空，遍历集合并计算作业量
            for (FmWork fmWork : fmWorkList) {
                type = fmWork.getType();
                if (StringHelper.isEmpty(type) || StringHelper.isEmpty(fmWork.getQualifiedLandarea())) {
                    continue;
                }
                if (workVolumeMap.containsKey(type)) {
                    currentArea = workVolumeMap.get(type);
                    if (ValidateUtils.isNullOrEmpty(currentArea)) {
                        currentArea = new BigDecimal(DEFAULT_BIG_DECIMAL_VALUE);
                    }
                    entityArea = new BigDecimal(fmWork.getQualifiedLandarea());
                    currentArea = currentArea.add(entityArea);
                } else {
                    entityArea = new BigDecimal(fmWork.getQualifiedLandarea());
                    currentArea = entityArea;
                }
                workVolumeMap.put(type, currentArea);
            }
        }

        if (MapUtils.isNotEmpty(workVolumeMap)) {
            for (Map.Entry<String, BigDecimal> entry : workVolumeMap.entrySet()) {
                String key = entry.getKey();
                currentArea = entry.getValue();
                if (currentArea.equals(BigDecimal.ZERO) || currentArea.doubleValue() == 0) {
                    continue;
                }
                WorkVolumeByType workVolumeByType = new WorkVolumeByType();
                if (WORK_TYPE_MAP.containsKey(key)) {
                    workVolumeByType.setName(WORK_TYPE_MAP.get(key));
                } else {
                    continue;
                }
                workVolumeByType.setValue(currentArea);
                resultList.add(workVolumeByType);
            }
        }
        return ResultUtil.success(result, resultList);
    }

    /**
     * 统计所有区域的下辖区域作业量分布
     *
     * @return
     */
    @Scheduled(fixedRate = FarmSysConstant.SCHEDULE_FIX_RATE)
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse statisticTotalWorkDataGroupByArea() {
        LocalDate currentDate = LocalDate.now(ZoneId.systemDefault());
        LocalDate queryBeginDate = currentDate.with(TemporalAdjusters.firstDayOfYear());
        LocalDate queryEndDate = currentDate.with(TemporalAdjusters.lastDayOfYear());
        if (currentDate.isBefore(queryEndDate)) {
            queryEndDate = currentDate;
        }
        QueryWrapper<JsSysOffice> jsSysOfficeQueryWrapper = new QueryWrapper<>();
        jsSysOfficeQueryWrapper.lt("office_type", 4);
        List<JsSysOffice> jsSysOfficeList = jsSysOfficeService.list(jsSysOfficeQueryWrapper);
        if (CollectionUtils.isEmpty(jsSysOfficeList)) {
            return ResultUtil.success();
        }
        String code, type;
        List<FmWorkStatisticByAreaVo> fmWorkStatisticByAreaVoList;
        List<WorkVolumeByArea> resultList = Lists.newLinkedList();
        QueryFmWorkVo queryCondition;
        boolean redisFlag;
        for (JsSysOffice jsSysOffice : jsSysOfficeList) {
            resultList.clear();
            queryCondition = new QueryFmWorkVo();
            queryCondition.setBeginDate(queryBeginDate);
            queryCondition.setEndDate(queryEndDate);
            if (StringHelper.isEmpty(jsSysOffice.getOfficeType()) || StringHelper.isEmpty(jsSysOffice.getOfficeCode())) {
                continue;
            }
            code = jsSysOffice.getOfficeCode();
            type = jsSysOffice.getOfficeType();
            switch (type) {
                case "1":
                    //省级机构
                    queryCondition.setProvince(code);
                    break;
                case "2":
                    //市级机构
                    queryCondition.setCity(code);
                    break;
                case "3":
                    //区县机构
                    queryCondition.setCounty(code);
                    break;
                default:
                    //乡镇机构
                    queryCondition.setTowns(code);
                    break;
            }
            fmWorkStatisticByAreaVoList = fmWorkService.selectStatisticByArea(queryCondition);
            if (CollectionUtils.isNotEmpty(fmWorkStatisticByAreaVoList)) {
                for (FmWorkStatisticByAreaVo fmWorkStatisticByAreaVo : fmWorkStatisticByAreaVoList) {
                    WorkVolumeByArea bean = new WorkVolumeByArea();
                    if (StringHelper.isEmpty(fmWorkStatisticByAreaVo.getName())) {
                        continue;
                    }
                    bean.setValue(fmWorkStatisticByAreaVo.getWorkQuantity());
                    bean.setName(fmWorkStatisticByAreaVo.getName());
                    resultList.add(bean);
                }
            }
            if (CollectionUtils.isNotEmpty(resultList)) {
                redisFlag = redisUtils.hSet(FarmSysConstant.REDIS_KEY_TOTAL_FM_WORK_BY_AREA, code, resultList);
                if (!redisFlag) {
                    throw new FarmException(CommonConstant.EX_DB_REDIS_ERROR, "系统缓存异常");
                }
            }
        }
        return ResultUtil.success();
    }

    /**
     * 按区域查询统计作业量
     *
     * @return
     */
    public RestResponse<List<WorkVolumeByArea>> getTotalWorkDataGroupByArea(BaseRequestVo<QueryFmWorkVo> vo) {
        RestResponse<List<WorkVolumeByArea>> result = new RestResponse();
        List<WorkVolumeByArea> resultList = Lists.newLinkedList();
        List<FmWorkStatisticByAreaVo> fmWorkStatisticByAreaVoList;
        String token = vo.getUserToken();
        String officeCode = getOfficeCodeByToken(token);
        boolean redisFlag;
        redisFlag = redisUtils.hHasKey(FarmSysConstant.REDIS_KEY_TOTAL_FM_WORK_BY_AREA, officeCode);
        if (redisFlag) {
            resultList = (List<WorkVolumeByArea>) redisUtils.hGet(FarmSysConstant.REDIS_KEY_TOTAL_FM_WORK_BY_AREA, officeCode);
            return ResultUtil.success(result, resultList);
        }
        JsSysUser sysUser = commonService.findUserByToken(token);
        if (ValidateUtils.isNullOrEmpty(sysUser)) {
            return ResultUtil.error(result, CommonConstant.ASSOCIATED_DATA_MISS, "找不到登录用户相关数据");
        }
        QueryFmWorkVo queryCondition = new QueryFmWorkVo();
        LocalDate currentDate = LocalDate.now(ZoneId.systemDefault());
        LocalDate queryBeginDate = currentDate.with(TemporalAdjusters.firstDayOfYear());
        LocalDate queryEndDate = currentDate.with(TemporalAdjusters.lastDayOfYear());
        if (currentDate.isBefore(queryEndDate)) {
            queryEndDate = currentDate;
        }
        queryCondition.setBeginDate(queryBeginDate);
        queryCondition.setEndDate(queryEndDate);
        if (SysRoleEnum.MASTER.getLevel().equals(sysUser.getSign())) {
            // 机构管理员 可以看到所辖区域内所有相关信息
            // GR代表个人用户
            String specialOfficeCode = "GR";
            if (StringHelper.isNotEmpty(officeCode) && !specialOfficeCode.equals(officeCode)) {
                JsSysOffice jsSysOffice = jsSysOfficeService.getById(officeCode);
                if (ValidateUtils.isNotNullOrEmpty(jsSysOffice) && StringHelper.isNotEmpty(jsSysOffice.getParentCodes())) {
                    String parentCodes = jsSysOffice.getParentCodes();
                    if (parentCodes.contains(CommonConstant.HARDWARE_RESPONSE_SPLIT_STR)) {
                        String[] parentCode = parentCodes.split(CommonConstant.HARDWARE_RESPONSE_SPLIT_STR);
                        int codeLength = parentCode.length;
                        switch (codeLength) {
                            case 1:
                                // 省级机构
                                queryCondition.setProvince(officeCode);
                                break;
                            case 2:
                                // 市级机构
                                queryCondition.setCity(officeCode);
                                break;
                            default:
                                // 区县机构
                                queryCondition.setCounty(officeCode);
                                break;
                        }
                    }
                }
            }
        }
        fmWorkStatisticByAreaVoList = fmWorkService.selectStatisticByArea(queryCondition);
        if (CollectionUtils.isNotEmpty(fmWorkStatisticByAreaVoList)) {
            for (FmWorkStatisticByAreaVo fmWorkStatisticByAreaVo : fmWorkStatisticByAreaVoList) {
                WorkVolumeByArea bean = new WorkVolumeByArea();
                if (StringHelper.isEmpty(fmWorkStatisticByAreaVo.getName())) {
                    continue;
                }
                bean.setValue(fmWorkStatisticByAreaVo.getWorkQuantity());
                bean.setName(fmWorkStatisticByAreaVo.getName());
                resultList.add(bean);
            }
        }
        return ResultUtil.success(result, resultList);
    }

    /**
     * 按区域以及作业类型查询作业量
     *
     * @return
     */
    public RestResponse<List<WorkVolumeByTypeAndArea>> getWorkQuantityByTypeAndArea(BaseRequestVo<QueryFmWorkVo> vo) {
        RestResponse<List<WorkVolumeByTypeAndArea>> result = new RestResponse<>();
        List<WorkVolumeByTypeAndArea> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        JsSysUser sysUser = commonService.findUserByToken(token);
        if (ValidateUtils.isNullOrEmpty(sysUser)) {
            return ResultUtil.success(result, resultList);
        }
        String officeCode = getOfficeCodeByToken(token);
        boolean redisFlag;
        if (StringHelper.isNotEmpty(officeCode)) {
            redisFlag = redisUtils.hHasKey(FarmSysConstant.REDIS_KEY_TOTAL_WORK_QUANTITY_BY_AREA_AND_TYPE, officeCode);
            if (redisFlag) {
                List<WorkVolumeByTypeAndArea> workVolumeByTypeAndAreaList = (List<WorkVolumeByTypeAndArea>) redisUtils.hGet(FarmSysConstant.REDIS_KEY_TOTAL_WORK_QUANTITY_BY_AREA_AND_TYPE, officeCode);
                if (CollectionUtils.isNotEmpty(workVolumeByTypeAndAreaList)) {
                    workVolumeByTypeAndAreaList.stream().forEach(workVolumeByTypeAndArea -> {
                        resultList.add(workVolumeByTypeAndArea);
                    });
                }
                return ResultUtil.success(result, resultList);
            }
        }
        return ResultUtil.success(result, resultList);
    }

    /**
     * 按周统计作业量
     *
     * @return
     */
    public RestResponse<List<BigDecimal>> getWorkQuantityDataByWeek(BaseRequestVo<QueryFmWorkVo> vo) {
        RestResponse<List<BigDecimal>> result = new RestResponse<>();
        List<BigDecimal> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        JsSysUser sysUser = commonService.findUserByToken(token);
        if (ValidateUtils.isNullOrEmpty(sysUser)) {
            return ResultUtil.success(result, resultList);
        }
        String officeCode = getOfficeCodeByToken(token);
        boolean redisFlag;
        if (StringHelper.isNotEmpty(officeCode)) {
            redisFlag = redisUtils.hHasKey(FarmSysConstant.REDIS_KEY_TOTAL_FM_WORK_BY_WEEK, officeCode);
            if (redisFlag) {
                String jsonString = (String) redisUtils.hGet(FarmSysConstant.REDIS_KEY_TOTAL_FM_WORK_BY_WEEK, officeCode);
                if (StringHelper.isNotEmpty(jsonString)) {
                    List<WorkVolumeByWeek> workVolumeByWeekList = JSON.parseArray(jsonString, WorkVolumeByWeek.class);
                    if (CollectionUtils.isNotEmpty(workVolumeByWeekList)) {
                        workVolumeByWeekList.stream().forEach(workVolumeByWeek -> {
                            resultList.add(workVolumeByWeek.getValue());
                        });
                    }
                    return ResultUtil.success(result, resultList);
                }
            }
        }
        return ResultUtil.success(result, resultList);
    }

    /**
     * 查询设备分布状况
     *
     * @return
     */
    public RestResponse<List<String[]>> getEquipmentDistribution(BaseRequestVo<QueryFmWorkVo> vo) {
        RestResponse<List<String[]>> result = new RestResponse<>();
        List<String[]> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        RestResponse<Set<String>> carIdResult = getCarIdsByToken(token);
        Set<String> carIdSet = carIdResult.getData();
        if (CollectionUtils.isEmpty(carIdSet)) {
            return ResultUtil.success(result, resultList);
        }
        boolean redisFlag;
        for (String carId : carIdSet) {
            redisFlag = redisUtils.hHasKey(FarmSysConstant.REDIS_KEY_EQUIPMENT_GPS_INFORMATION, carId);
            if (redisFlag) {
                String jsonString = (String) redisUtils.hGet(FarmSysConstant.REDIS_KEY_EQUIPMENT_GPS_INFORMATION, carId);
                EquipmentDistributionVo equipmentDistributionVo = JSON.toJavaObject(JSON.parseObject(jsonString), EquipmentDistributionVo.class);
                String[] geo = new String[3];
                geo[0] = equipmentDistributionVo.getLon();
                geo[1] = equipmentDistributionVo.getLat();
                geo[2] = equipmentDistributionVo.getAltitude();
                resultList.add(geo);
            }
        }
        return ResultUtil.success(result, resultList);
    }

    /**
     * 查询设备归属分布
     *
     * @param vo
     * @return
     */
    public RestResponse<List<DeviceAccessStatisticsVo>> statisticsDevice(@Validated BaseRequestVo<QueryFmCarVo> vo) {
        RestResponse<List<DeviceAccessStatisticsVo>> result;
        String token = vo.getUserToken();
        result = fmCarService.getDeviceAccessStatisticsResult(token);
        return result;
    }

    /**
     * 统计活跃设备分布情况
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
//    @Scheduled(fixedRate = FarmSysConstant.SCHEDULE_FIX_RATE)
    public BaseResponse equipmentActiveLocation() {
        List<FmCar> fmCarList = fmCarService.list();
        if (CollectionUtils.isEmpty(fmCarList)) {
            return ResultUtil.success();
        }
        String carId;
        LocalDateTime activeTime = LocalDateTime.now(ZoneId.systemDefault());
        QuerySimpleGpsVo querySimpleGpsVo = new QuerySimpleGpsVo();
        querySimpleGpsVo.setLastQueryTime(activeTime);
        for (FmCar fmCar : fmCarList) {
            if (StringHelper.isNotEmpty(fmCar.getCarId())) {
                carId = fmCar.getCarId();
                SimpleGps simpleGps = simpleGpsService.getLastLocation(querySimpleGpsVo);
                if (ValidateUtils.isNotNullOrEmpty(simpleGps)) {
                    EquipmentDistributionVo equipmentDistributionVo = new EquipmentDistributionVo();
                    equipmentDistributionVo.setLon(simpleGps.getGpsLon());
                    equipmentDistributionVo.setLat(simpleGps.getGpsLat());
                    redisUtils.hSet(FarmSysConstant.REDIS_KEY_EQUIPMENT_GPS_INFORMATION, carId, JSON.toJSONString(equipmentDistributionVo));
                }
            }
        }
        return ResultUtil.success();
    }

    /**
     * 统计所有区域设备分布状况
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(fixedRate = FarmSysConstant.SCHEDULE_FIX_RATE)
    public BaseResponse statisticEquipmentDistribution() {
        QueryWrapper<FmCar> fmCarQueryWrapper;
        QueryWrapper<JsSysOffice> jsSysOfficeQueryWrapper = new QueryWrapper<>();
        jsSysOfficeQueryWrapper.lt("office_type", 4);
        List<JsSysOffice> jsSysOfficeList = jsSysOfficeService.list(jsSysOfficeQueryWrapper);
        List<FmCar> fmCarList;
        if (CollectionUtils.isEmpty(jsSysOfficeList)) {
            return ResultUtil.success();
        }
        String officeCode, officeType;
        boolean redisFlag;
        for (JsSysOffice jsSysOffice : jsSysOfficeList) {
            officeCode = jsSysOffice.getOfficeCode();
            officeType = jsSysOffice.getOfficeType();
            if (StringHelper.isEmpty(officeCode)) {
                continue;
            }
            fmCarQueryWrapper = new QueryWrapper<>();
            switch (officeType) {
                case "1":
                    fmCarQueryWrapper.eq("province", officeCode);
                    break;
                case "2":
                    fmCarQueryWrapper.eq("city", officeCode);
                    break;
                case "3":
                    fmCarQueryWrapper.eq("county", officeCode);
                    break;
                default:
                    fmCarQueryWrapper.eq("towns", officeCode);
                    break;
            }
            fmCarList = fmCarService.list(fmCarQueryWrapper);
            if (CollectionUtils.isEmpty(fmCarList)) {
                continue;
            }
            redisFlag = redisUtils.hSet(FarmSysConstant.REDIS_KEY_EQUIPMENT_DISTRIBUTION, officeCode, JSON.toJSONString(fmCarList));
            if (!redisFlag) {
                LOG.error("系统缓存异常！");
            }
        }
        return ResultUtil.success();
    }

    /**
     * 统计所有区域周作业量
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(fixedRate = FarmSysConstant.SCHEDULE_FIX_RATE)
    public BaseResponse statisticWorkQuantityPerWeek() {
        LocalDate currentDate = LocalDate.now(ZoneId.systemDefault());
        LocalDate queryBeginDate = currentDate.with(DayOfWeek.MONDAY);
        LocalDate queryEndDate = currentDate.with(DayOfWeek.SUNDAY);
        QueryWrapper<FmWork> fmWorkQueryWrapper;
        QueryWrapper<FmCar> fmCarQueryWrapper;
        QueryWrapper<JsSysOffice> jsSysOfficeQueryWrapper = new QueryWrapper<>();
        jsSysOfficeQueryWrapper.lt("office_type", 4);
        List<JsSysOffice> jsSysOfficeList = jsSysOfficeService.list(jsSysOfficeQueryWrapper);
        List<FmCar> fmCarList;
        List<FmWork> fmWorkList;
        int totalSize = 7;
        if (CollectionUtils.isEmpty(jsSysOfficeList)) {
            return ResultUtil.success();
        }
        String officeCode, officeType;
        Set<String> fmCarIdSet = Sets.newTreeSet();
        BigDecimal entityArea;
        BigDecimal currentArea;
        Map<String, BigDecimal> workVolumeMap = Maps.newHashMap();
        Map<String, BigDecimal> initWorkVolumeMap = Maps.newHashMap();
        for (int i = 0; i < totalSize; i++) {
            initWorkVolumeMap.put(String.valueOf(i), new BigDecimal("0"));
        }
        List<WorkVolumeByWeek> resultList = Lists.newLinkedList();
        boolean redisFlag;
        for (JsSysOffice jsSysOffice : jsSysOfficeList) {
            fmCarIdSet.clear();
            workVolumeMap.clear();
            workVolumeMap.putAll(initWorkVolumeMap);
            resultList.clear();
            fmWorkQueryWrapper = new QueryWrapper<>();
            fmWorkQueryWrapper.between("work_date", queryBeginDate, queryEndDate);
            fmCarQueryWrapper = new QueryWrapper<>();
            officeCode = jsSysOffice.getOfficeCode();
            officeType = jsSysOffice.getOfficeType();
            if (StringHelper.isEmpty(officeCode)) {
                continue;
            }
            switch (officeType) {
                case "1":
                    fmCarQueryWrapper.eq("province", officeCode);
                    break;
                case "2":
                    fmCarQueryWrapper.eq("city", officeCode);
                    break;
                case "3":
                    fmCarQueryWrapper.eq("county", officeCode);
                    break;
                default:
                    fmCarQueryWrapper.eq("towns", officeCode);
                    break;
            }
            fmCarList = fmCarService.list(fmCarQueryWrapper);
            if (CollectionUtils.isNotEmpty(fmCarList)) {
                fmCarList.stream().forEach(item -> fmCarIdSet.add(item.getCarId()));
            }
            if (CollectionUtils.isNotEmpty(fmCarIdSet)) {
                fmWorkQueryWrapper.in("car_id", fmCarIdSet);
            }
            fmWorkList = fmWorkService.list(fmWorkQueryWrapper);
            if (CollectionUtils.isNotEmpty(fmWorkList)) {
                LocalDate workDate;
                int plusDay;
                String mapKey;
                // 如果FMWork集合不为空，遍历集合并计算作业量
                for (FmWork fmWork : fmWorkList) {
                    workDate = fmWork.getWorkDate().toLocalDate();
                    if (ValidateUtils.isNullOrEmpty(workDate) || StringHelper.isEmpty(fmWork.getQualifiedLandarea())) {
                        continue;
                    }
                    plusDay = (int) (workDate.toEpochDay() - queryBeginDate.toEpochDay());
                    mapKey = String.valueOf(plusDay);
                    if (workVolumeMap.containsKey(mapKey)) {
                        currentArea = workVolumeMap.get(mapKey);
                        entityArea = new BigDecimal(fmWork.getQualifiedLandarea());
                        currentArea = currentArea.add(entityArea);
                    } else {
                        entityArea = new BigDecimal(fmWork.getQualifiedLandarea());
                        currentArea = entityArea;
                    }
                    workVolumeMap.put(mapKey, currentArea);
                }
            }
            if (MapUtils.isNotEmpty(workVolumeMap)) {
                for (Map.Entry<String, BigDecimal> entry : workVolumeMap.entrySet()) {
                    String key = entry.getKey();
                    currentArea = entry.getValue();
                    WorkVolumeByWeek workVolumeByWeek = new WorkVolumeByWeek();
                    workVolumeByWeek.setName(key);
                    workVolumeByWeek.setDayOfWeek(Integer.parseInt(key) + 1);
                    workVolumeByWeek.setWorkDate(queryBeginDate.plusDays(Integer.parseInt(key)));
                    workVolumeByWeek.setValue(currentArea);
                    resultList.add(workVolumeByWeek);
                }
            }
            if (CollectionUtils.isNotEmpty(resultList)) {
                Collections.sort(resultList);
                redisFlag = redisUtils.hSet(FarmSysConstant.REDIS_KEY_TOTAL_FM_WORK_BY_WEEK, officeCode, JSON.toJSONString(resultList));
                if (!redisFlag) {
                    LOG.error("系统缓存异常！");
                }
            }
        }
        return ResultUtil.success();
    }

    /**
     * 统计所有区域的不同类型作业的完成量
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(fixedRate = FarmSysConstant.SCHEDULE_FIX_RATE)
    public BaseResponse statisticWorkQuantityByTypePerArea() {
        BaseResponse result = new BaseResponse();
        LocalDate currentDate = LocalDate.now(ZoneId.systemDefault());
        LocalDate queryBeginDate = currentDate.with(TemporalAdjusters.firstDayOfYear());
        LocalDate queryEndDate = currentDate.with(TemporalAdjusters.lastDayOfYear());
        if (currentDate.isBefore(queryEndDate)) {
            queryEndDate = currentDate;
        }
        QueryWrapper<FmWork> fmWorkQueryWrapper;
        QueryWrapper<FmCar> fmCarQueryWrapper;
        QueryWrapper<JsSysOffice> jsSysOfficeQueryWrapper = new QueryWrapper<>();
        jsSysOfficeQueryWrapper.lt("office_type", 4);
        List<JsSysOffice> jsSysOfficeList = jsSysOfficeService.list(jsSysOfficeQueryWrapper);
        List<FmCar> fmCarList;
        List<FmWork> fmWorkList;
        if (CollectionUtils.isEmpty(jsSysOfficeList)) {
            return ResultUtil.success();
        }
        String officeCode, officeType;
        Set<String> fmCarIdSet = Sets.newTreeSet();
        BigDecimal entityArea;
        BigDecimal currentArea;
        Map<String, StatisticTotalWorkQuantityByAreaAndType> workVolumeMap = Maps.newHashMap();
        Map<String, FmCar> fmCarMap = Maps.newLinkedHashMap();
        List<WorkVolumeByTypeAndArea> resultList = Lists.newLinkedList();
        boolean redisFlag;
        StatisticTotalWorkQuantityByAreaAndType statisticTotalWorkQuantityByAreaAndType;
        StringBuilder itemKey = new StringBuilder();
        int count;
        for (JsSysOffice jsSysOffice : jsSysOfficeList) {
            fmCarIdSet.clear();
            workVolumeMap.clear();
            resultList.clear();
            fmCarMap.clear();
            fmWorkQueryWrapper = new QueryWrapper<>();
            fmWorkQueryWrapper.between("work_date", queryBeginDate, queryEndDate);
            fmCarQueryWrapper = new QueryWrapper<>();
            officeCode = jsSysOffice.getOfficeCode();
            officeType = jsSysOffice.getOfficeType();
            if (StringHelper.isEmpty(officeCode)) {
                continue;
            }
            switch (officeType) {
                case "1":
                    fmCarQueryWrapper.eq("province", officeCode);
                    break;
                case "2":
                    fmCarQueryWrapper.eq("city", officeCode);
                    break;
                case "3":
                    fmCarQueryWrapper.eq("county", officeCode);
                    break;
                default:
                    fmCarQueryWrapper.eq("towns", officeCode);
                    break;
            }
            count = fmCarService.count(fmCarQueryWrapper);
            if (0 == count){
                continue;
            }
            fmCarList = fmCarService.list(fmCarQueryWrapper);
            fmCarList.stream().forEach(item -> fmCarIdSet.add(item.getCarId()));
            fmCarMap = fmCarList.stream().collect(Collectors.toConcurrentMap(FmCar::getCarId, a -> a, (k1, k2) -> k1));
            if (CollectionUtils.isNotEmpty(fmCarIdSet)) {
                fmWorkQueryWrapper.in("car_id", fmCarIdSet);
            }
            fmWorkList = fmWorkService.list(fmWorkQueryWrapper);
            if (CollectionUtils.isNotEmpty(fmWorkList)) {
                String type;
                String carId;
                FmCar fmCar = null;
                JsSysOffice leafJsSysOffice;
                String areaName = null, areaCode;
                // 如果FMWork集合不为空，遍历集合并计算作业量
                for (FmWork fmWork : fmWorkList) {
                    statisticTotalWorkQuantityByAreaAndType = new StatisticTotalWorkQuantityByAreaAndType();
                    itemKey.delete(0, itemKey.length());
                    type = fmWork.getType();
                    carId = fmWork.getCarId();
                    if (StringHelper.isEmpty(type) || StringHelper.isEmpty(carId)) {
                        continue;
                    }
                    if (fmCarMap.containsKey(carId)) {
                        fmCar = fmCarMap.get(carId);
                    }
                    if (ValidateUtils.isNullOrEmpty(fmCar)) {
                        continue;
                    }
                    switch (officeType) {
                        case "1":
                            areaCode = fmCar.getCity();
                            break;
                        case "2":
                            areaCode = fmCar.getCounty();
                            break;
                        case "3":
                            areaCode = fmCar.getTowns();
                            break;
                        default:
                            areaCode = fmCar.getProvince();
                            break;
                    }
                    leafJsSysOffice = jsSysOfficeService.getByOfficeCode(areaCode);
                    if (ValidateUtils.isNotNullOrEmpty(leafJsSysOffice)) {
                        areaName = leafJsSysOffice.getOfficeName();
                    }
                    itemKey.append(areaCode).append(CommonConstant.SYSTEM_SPLIT_STR).append(type);
                    if (StringHelper.isEmpty(type) || StringHelper.isEmpty(fmWork.getQualifiedLandarea())) {
                        continue;
                    }
                    if (workVolumeMap.containsKey(itemKey.toString())) {
                        currentArea = workVolumeMap.get(itemKey.toString()).getWorkQuantity();
                        if (ValidateUtils.isNullOrEmpty(currentArea)) {
                            currentArea = new BigDecimal(DEFAULT_BIG_DECIMAL_VALUE);
                        }
                        entityArea = new BigDecimal(fmWork.getQualifiedLandarea());
                        currentArea = currentArea.add(entityArea);
                    } else {
                        entityArea = new BigDecimal(fmWork.getQualifiedLandarea());
                        currentArea = entityArea;
                    }
                    statisticTotalWorkQuantityByAreaAndType.setArea(areaName);
                    statisticTotalWorkQuantityByAreaAndType.setType(type);
                    statisticTotalWorkQuantityByAreaAndType.setWorkQuantity(currentArea);
                    workVolumeMap.put(itemKey.toString(), statisticTotalWorkQuantityByAreaAndType);
                }
            }
            if (MapUtils.isNotEmpty(workVolumeMap)) {
                for (Map.Entry<String, StatisticTotalWorkQuantityByAreaAndType> entry : workVolumeMap.entrySet()) {
                    StatisticTotalWorkQuantityByAreaAndType entity = entry.getValue();
                    currentArea = entity.getWorkQuantity();
                    if (currentArea.equals(BigDecimal.ZERO) || currentArea.doubleValue() == 0) {
                        continue;
                    }
                    WorkVolumeByTypeAndArea workVolumeByTypeAndArea = new WorkVolumeByTypeAndArea();
                    workVolumeByTypeAndArea.setName(entity.getName());
                    workVolumeByTypeAndArea.setValue(entity.getWorkQuantity());
                    resultList.add(workVolumeByTypeAndArea);
                }
            }
            if (CollectionUtils.isNotEmpty(resultList)) {
                redisFlag = redisUtils.hSet(FarmSysConstant.REDIS_KEY_TOTAL_WORK_QUANTITY_BY_AREA_AND_TYPE, officeCode, resultList);
                if (!redisFlag) {
                    LOG.error("系统缓存异常！");
                }
            }
        }
        return result;
    }

    /**
     * 查询昨日指定区域的地块数量
     *
     * @param vo
     * @return
     */
    public RestResponse<List<Integer>> getAreaFmWorkDetailCountYesterday(@Validated BaseRequestVo<QueryFmWorkDetailsVo> vo) {
        RestResponse<List<Integer>> result = new RestResponse<>();
        List<Integer> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        String officeCode = getOfficeCodeByToken(token);
        if (StringHelper.isEmpty(officeCode)) {
            return ResultUtil.error(result, CommonConstant.ASSOCIATED_DATA_MISS, "未找到officeCode");
        }
        RestResponse<Integer> transformResult = fmWorkDetailsService.getAreaFmWorkDetailsCount(officeCode);
        if (!transformResult.isSuccess()) {
            return ResultUtil.error(result, transformResult.getCode(), transformResult.getErrorMessage());
        }
        Integer data = transformResult.getData();
        resultList.add(data);
        return ResultUtil.success(result, resultList);
    }

    /**
     * 查询昨日指定区域的作业量
     *
     * @param vo
     * @return
     */
    public RestResponse<List<BigDecimal>> getTotalWorkQuantityByAreaYesterday(@Validated BaseRequestVo<QueryFmWorkVo> vo) {
        RestResponse<BigDecimal> transformResult = new RestResponse<>();
        RestResponse<List<BigDecimal>> result = new RestResponse<>();
        List<BigDecimal> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        String officeCode = getOfficeCodeByToken(token);
        if (StringHelper.isEmpty(officeCode)) {
            return ResultUtil.error(transformResult, CommonConstant.ASSOCIATED_DATA_MISS, "未找到officeCode");
        }
        transformResult = fmWorkService.getTotalWorkQuantityByAreaYesterday(officeCode);
        if (!transformResult.isSuccess()) {
            return ResultUtil.error(result, transformResult.getCode(), transformResult.getErrorMessage());
        }
        BigDecimal data = transformResult.getData();
        resultList.add(data);
        return ResultUtil.success(result, resultList);
    }

    /**
     * 查询昨日指定区域的活动设备数量
     *
     * @param vo
     * @return
     */
    public RestResponse<List<Integer>> getActiveDeviceCountYesterday(@Validated BaseRequestVo<QueryFmWorkDetailsVo> vo) {
        RestResponse<List<Integer>> result = new RestResponse<>();
        List<Integer> resultList = Lists.newLinkedList();
        String token = vo.getUserToken();
        String officeCode = getOfficeCodeByToken(token);
        if (StringHelper.isEmpty(officeCode)) {
            return ResultUtil.error(result, CommonConstant.ASSOCIATED_DATA_MISS, "未找到officeCode");
        }
        RestResponse<Integer> transformResult = fmWorkDetailsService.getAreaFmWorkDetailsCount(officeCode);
        if (!transformResult.isSuccess()) {
            return ResultUtil.error(result, transformResult.getCode(), transformResult.getErrorMessage());
        }
        Integer data = transformResult.getData();
        resultList.add(data);
        return ResultUtil.success(result, resultList);
    }

    /**
     * 根据token查询用户所在机构的区域代码
     *
     * @param token
     * @return
     */
    private String getOfficeCodeByToken(String token) {
        String officeCode = null;
        JsSysUser sysUser = commonService.findUserByToken(token);
        if (ValidateUtils.isNullOrEmpty(sysUser)) {
            return null;
        }
        String userCode = sysUser.getUserCode();
        JsSysEmployee jsSysEmployee = jsSysEmployeeService.getById(userCode);
        if (ValidateUtils.isNotNullOrEmpty(jsSysEmployee) && StringHelper.isNotEmpty(jsSysEmployee.getOfficeCode())) {
            officeCode = jsSysEmployee.getOfficeCode();
        }
        return officeCode;
    }

    /**
     * 根据登录token查询主机工作记录
     *
     * @param token
     * @return
     */
    private List<FmWork> getFmWorkListByToken(String token) {
        List<FmWork> result = Lists.newLinkedList();
        RestResponse<Set<String>> fmCarResult = getCarIdsByToken(token);
        if (!fmCarResult.isSuccess()) {
            LOG.error("按token查询主机号失败！ {} ", fmCarResult.getErrorMessage());
            return result;
        }
        LocalDate currentDate = LocalDate.now(ZoneId.systemDefault());
        LocalDate queryBeginDate = currentDate.with(TemporalAdjusters.firstDayOfYear());
        LocalDate queryEndDate = currentDate.with(TemporalAdjusters.lastDayOfYear());
        if (currentDate.isBefore(queryEndDate)) {
            queryEndDate = currentDate;
        }
        Set<String> fmCarIdSet = fmCarResult.getData();
        QueryWrapper<FmWork> fmWorkQueryWrapper = new QueryWrapper<>();
        fmWorkQueryWrapper.between("work_date", queryBeginDate, queryEndDate);
        if (CollectionUtils.isNotEmpty(fmCarIdSet)) {
            fmWorkQueryWrapper.in("car_id", fmCarIdSet);
        }
        result = fmWorkService.list(fmWorkQueryWrapper);
        return result;
    }

    /**
     * 根据token查询可见主机号
     *
     * @param token
     * @return
     */
    private RestResponse<Set<String>> getCarIdsByToken(@NotBlank String token) {
        RestResponse<Set<String>> result = new RestResponse<>();
        JsSysUser sysUser = commonService.findUserByToken(token);
        if (ValidateUtils.isNullOrEmpty(sysUser)) {
            return ResultUtil.error(result, CommonConstant.ASSOCIATED_DATA_MISS, "找不到登录用户相关数据");
        }
        String officeCode = getOfficeCodeByToken(token);
        QueryWrapper<FmCar> fmCarQueryWrapper = new QueryWrapper<>();
        Set<String> fmCarIdSet = Sets.newTreeSet();
        List<FmCar> fmCarList;
        if (SysRoleEnum.MASTER.getLevel().equals(sysUser.getSign())) {
            // 机构管理员 可以看到所辖区域内所有相关信息
            // GR代表个人用户
            String specialOfficeCode = "GR";
            if (StringHelper.isNotEmpty(officeCode) && !specialOfficeCode.equals(officeCode)) {
                JsSysOffice jsSysOffice = jsSysOfficeService.getById(officeCode);
                if (ValidateUtils.isNotNullOrEmpty(jsSysOffice) && StringHelper.isNotEmpty(jsSysOffice.getParentCodes())) {
                    String parentCodes = jsSysOffice.getParentCodes();
                    if (parentCodes.contains(CommonConstant.HARDWARE_RESPONSE_SPLIT_STR)) {
                        String[] parentCode = parentCodes.split(CommonConstant.HARDWARE_RESPONSE_SPLIT_STR);
                        int codeLength = parentCode.length;
                        switch (codeLength) {
                            case 1:
                                // 省级机构
                                fmCarQueryWrapper.eq("province", officeCode);
                                break;
                            case 2:
                                // 市级机构
                                fmCarQueryWrapper.eq("province", parentCode[1]).eq("city", officeCode);
                                break;
                            default:
                                // 区县机构
                                fmCarQueryWrapper.eq("province", parentCode[1]).eq("city", parentCode[2])
                                        .eq("county", officeCode);
                                break;
                        }
                    }
                }
            }
            fmCarList = fmCarService.list(fmCarQueryWrapper);
            if (CollectionUtils.isNotEmpty(fmCarList)) {
                fmCarList.stream().forEach(item -> fmCarIdSet.add(item.getCarId()));
            }
        }
        return ResultUtil.success(result, fmCarIdSet);
    }

    /**
     * ----------------------------- 分割线 以下是setter方法，用于替换field上的@Autowired -----------------------------
     **/

    @Autowired
    @Lazy
    public void setCommonService(CommonService commonService) {
        this.commonService = commonService;
    }

    @Autowired
    @Lazy
    public void setFmCarService(IFmCarService fmCarService) {
        this.fmCarService = fmCarService;
    }

    @Autowired
    @Lazy
    public void setFmWorkTotalService(IFmWorkTotalService fmWorkTotalService) {
        this.fmWorkTotalService = fmWorkTotalService;
    }

    @Autowired
    @Lazy
    public void setFmWorkService(IFmWorkService fmWorkService) {
        this.fmWorkService = fmWorkService;
    }

    @Autowired
    @Lazy
    public void setFmWorkDetailsService(IFmWorkDetailsService fmWorkDetailsService) {
        this.fmWorkDetailsService = fmWorkDetailsService;
    }

    @Autowired
    @Lazy
    public void setJsSysEmployeeService(IJsSysEmployeeService jsSysEmployeeService) {
        this.jsSysEmployeeService = jsSysEmployeeService;
    }

    @Autowired
    @Lazy
    public void setJsSysOfficeService(IJsSysOfficeService jsSysOfficeService) {
        this.jsSysOfficeService = jsSysOfficeService;
    }

    @Autowired
    @Lazy
    public void setRedisUtils(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    @Autowired
    @Lazy
    public void setSimpleGpsService(ISimpleGpsService simpleGpsService) {
        this.simpleGpsService = simpleGpsService;
    }
}
